//
// Created by martin on 2/24/22.
//

#ifndef MUDUO_BLOCKINGQUEUE_H
#define MUDUO_BLOCKINGQUEUE_H

#include "muduo/base/noncopyable.h"
#include "muduo/base/Mutex.h"
#include "muduo/base/Condition.h"

#include <deque>

namespace muduo
{

template <typename T>
class BlockingQueue : noncopyable
{
public:
    using queue_type = std::deque<T>;

    BlockingQueue()
    : mutex_(),
      notEmpty_(mutex_),
      queue_()
    {
    }

    void put(const T& x)
    {
        MutexLockGuard lock(mutex_);
        queue_.push_back(x);
        notEmpty_.notify(); // wait morphing saves us
        // http://www.domaigne.com/blog/computing/condvars-signal-with-mutex-locked-or-not/
    }

    void put(T&& x)
    {
        MutexLockGuard lock(mutex_);
        queue_.push_back(std::move(x));
        notEmpty_.notify();
    }

    T take()
    {
        MutexLockGuard lock(mutex_);
        // always use a while-loop, due to spurious wakeup
        while (queue_.empty())
        {
            notEmpty_.wait();
        }
        assert(!queue_.empty());
        T front(std::move(queue_.front()));
        queue_.pop_front();
        return front;
    }

    queue_type drain()
    {
        std::deque<T> queue;
        {
            MutexLockGuard lock(mutex_);
            queue = std::move(queue_);
            assert(queue_.empty());
        }
        return queue;
    }

    size_t size() const
    {
        MutexLockGuard lock(mutex_);
        return queue_.size();
    }
private:
    mutable MutexLock mutex_;
    Condition notEmpty_ GUARDED_BY(mutex_);
    queue_type queue_ GUARDED_BY(mutex_);
};

} // namespace muduo

#endif //MUDUO_BLOCKINGQUEUE_H
